#include "scene.h"
#include <stdio.h>
#include <algorithm>

Scene::~Scene() {
   m_entities.clear();
   m_x_entities.clear();
   m_y_entities.clear();
}

void Scene::add(uint32_t id, int x, int y, uint32_t radius) {
    auto it = m_entities.find(id);
    if (it != m_entities.end()) {
        return move(id, x, y);
    }
    printf("add position[%u(%d, %d)]\n", id, x, y);

    m_entities.emplace(id, std::make_shared<Entity>(id, radius, x, y));
    it = m_entities.find(id);
    if (it == m_entities.end()) return;

    auto sit = std::find_if(m_x_entities.begin(), m_x_entities.end(), [x](const std::shared_ptr<Entity>& item) {
        return item->x > x;
    });
    it->second->x_pos = m_x_entities.insert(sit, it->second);

    auto tit = std::find_if(m_y_entities.begin(), m_y_entities.end(), [y](const std::shared_ptr<Entity>& item) {
        return item->y > y;
    });
    it->second->y_pos = m_y_entities.insert(tit, it->second);

    EntityMap new_map;
    getRangeSet(it->second, new_map);

    for (const auto& item : new_map) {
        printf("%u(%d, %d)\t Enter, send msg to obj %u(%d, %d)\n", id, x, y, (item.second)->id, (item.second)->x, (item.second)->y);
        printf("%u(%d, %d)\t Enter, send msg to obj %u(%d, %d)\n", (item.second)->id, (item.second)->x, (item.second)->y, id, x, y);
    }
    printf("add end------------------------------\n");
}

void Scene::updateEntityPos(std::shared_ptr<Entity>& entity, int x, int y) {
    int old_x = entity->x;
    int old_y = entity->y;

    entity->x = x;
    entity->y = y;

    auto end_it = m_x_entities.end();
    auto begin_it = m_x_entities.begin();
    if (old_x > x) {
        end_it =  entity->x_pos;
    } else if (x < old_x) {
        begin_it = entity->x_pos;
    }

    if (old_y != x) {
        auto it = std::find_if(begin_it, end_it, [x](const std::shared_ptr<Entity>& item) {
            return item->x > x;
        });
        m_x_entities.erase(entity->x_pos);
        entity->x_pos = m_x_entities.insert(it, entity);
    }


    end_it = m_y_entities.end();
    begin_it = m_y_entities.begin();
    if (old_y > y) {
        end_it = entity->y_pos;
    } else if (old_y < y) {
        begin_it = entity->y_pos;
    }

    if (old_y != y) {
        auto it = std::find_if(begin_it, end_it, [y](const std::shared_ptr<Entity>& item) {
            return item->y > y;
        });
        m_y_entities.erase(entity->y_pos);
        entity->y_pos = m_y_entities.insert(it, entity);
    }
}

void Scene::move(uint32_t id, int x, int y) {
    auto sit = m_entities.find(id);
    if (sit == m_entities.end()) return;

    printf("move id:%u x(%d->%d) y(%d->%d)\n", id, (sit->second)->x, x, (sit->second)->y, y);

    EntityMap old_maps, new_maps;
    getRangeSet(sit->second, old_maps);
    updateEntityPos(sit->second, x, y);
    getRangeSet(sit->second, new_maps);

    for (const auto& it : old_maps) {
        auto sit = new_maps.find(it.first);
        if (sit == new_maps.end()) {
            printf("send [%u:(%d, %d)]\t leave msg to [%u: (%d, %d)]\n", it.first, (it.second)->x, (it.second)->y, id, x, y);
            printf("send [%u:(%d, %d)]\t leave msg to [%u: (%d, %d)]\n", id, x, y, it.first, (it.second)->x, (it.second)->y);
        } else {
            printf("send [%u:(%d, %d)]\t move msg to [%u: (%d, %d)]\n", id, x, y, it.first, (it.second)->x, (it.second)->y);
        }
    }

    for (const auto& it : new_maps) {
        auto sit = old_maps.find(it.first);
        if (sit == old_maps.end()) {
            printf("send [%u:(%d, %d)]\t enter msg to [%u: (%d, %d)]\n", it.first, (it.second)->x, (it.second)->y, id, x, y);
            printf("send [%u:(%d, %d)]\t enter msg to [%u: (%d, %d)]\n", id, x, y, it.first, (it.second)->x, (it.second)->y);
        }
    }

    printf("move end-----------------------------\n");
}

void Scene::getRangeSet(const std::shared_ptr<Entity>& entity, EntityMap& maps) {
    EntityMap x_maps;
    auto it = entity->x_pos;
    while (it != m_x_entities.end()) {
        if ((*it)->x - entity->x > entity->radius) break;
        if (it != entity->x_pos) {
            x_maps[(*it)->id] = *it;
        }
        ++it;
    }

    it = entity->x_pos;
    while (it != m_x_entities.begin()) {
        --it;
        if (entity->x - (*it)->x > entity->radius) break;

        x_maps[(*it)->id] = *it;
    }

    it = entity->y_pos;
    while (it != m_y_entities.end()) {
        if ((*it)->y - entity->y > entity->radius) break;
        if (it == entity->y_pos) {
            ++it;
            continue;
        }
        auto sit = x_maps.find((*it)->id);
        if (sit != x_maps.end()) {
            maps[(*it)->id] = *it;
        }

        ++it;
    }

    it = entity->y_pos;
    while (it != m_y_entities.begin()) {
        --it;
        if (entity->y - (*it)->y > entity->radius) break;

        auto sit = x_maps.find((*it)->id);
        if (sit == x_maps.end()) continue;

        maps[(*it)->id] = *it;
    }
}

void Scene::leave(uint32_t id) {
    auto sit = m_entities.find(id);
    if (sit == m_entities.end()) return;

    printf("id:%d leave\n", id);

    EntityMap maps;
    getRangeSet(sit->second, maps);
    for (const auto& it : maps) {
        printf("send [%u:(%d, %d)]\t leave msg to [%u: (%d, %d)]\n", it.first, (it.second)->x, (it.second)->y, id, (sit->second)->x, (sit->second)->y);
        printf("send [%u:(%d, %d)]\t leave msg to [%u: (%d, %d)]\n", id, (sit->second)->x, (sit->second)->y, it.first, (it.second)->x, (it.second)->y);
    }

    m_x_entities.erase((sit->second)->x_pos);
    m_y_entities.erase((sit->second)->y_pos);
    m_entities.erase(sit);

    printf("leave end--------------------------\n");
}
